﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace WSBackup
{

    class WSStream : Stream
    {
        private enum StreamType { Memory, File }

        private const int MaximumLengthInMemory = 65536 * 1;

        private FileStream m_FileStream;
        private MemoryStream m_MemoryStream;
        private StreamType m_StreamType = StreamType.Memory;

        public string FileName { get; set; }
        public string ForFile { get; set; }

        public WSStream(string FileName, string ForFile)
        {
            //Debug.WriteLine("Create WSStream " + FileName + " for " + ForFile);
            this.FileName = FileName;
            this.ForFile = ForFile;
            this.m_MemoryStream = new MemoryStream();
        }

        public void CopyFromBegin(Stream sourceStream, OnProgress eh)
        {
            sourceStream.Seek(0, SeekOrigin.Begin);
            CopyFrom(sourceStream, eh);
        }

        public void CopyFrom(Stream sourceStream, OnProgress eh)
        {
            int total = (int)sourceStream.Length;
            int current = 0;
            int bytes;
            var buffer = new byte[FileArchiver.buffer_length];
            var last = DateTime.Now.AddMilliseconds(100);
            while ((bytes = sourceStream.Read(buffer, 0, FileArchiver.buffer_length)) > 0)
            {
                this.Write(buffer, 0, bytes);
                current += bytes;
                if (eh != null && DateTime.Now > last)
                {
                    eh(string.Format("Copying {0} / {1} {2:P2}", Util.FormatBytes(current), Util.FormatBytes(total), (float)current / total));
                    last = DateTime.Now.AddMilliseconds(250);
                }
            }
            if (eh != null)
            {
                eh(string.Format("Copying {0} / {1} {2:P2}", Util.FormatBytes(current), Util.FormatBytes(total), (float)current / total));
            }
        }

        /// <summary>
        /// Writes the entire stream into destination, regardless of Position, which remains unchanged.
        /// </summary>
        /// <param name="destination">The stream to write the content of this stream to</param>
        public void WriteTo(Stream destination)
        {
            var pos = Position;
            this.Position = 0;
            this.CopyTo(destination);
            this.Position = pos;
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override long Position
        {
            get { return Stream.Position; }
            set { Stream.Position = value; }
        }

        public override void Flush()
        {
            Stream.Flush();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return Stream.Read(buffer, offset, count);
        }

        public override int ReadByte()
        {
            return Stream.ReadByte();
        }

        private Stream Stream
        {
            get
            {
                if (this.m_StreamType == StreamType.Memory)
                    return m_MemoryStream;
                return m_FileStream;
            }
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            if (this.m_StreamType == StreamType.Memory && m_MemoryStream.Position + count > MaximumLengthInMemory)
            {
                Debug.WriteLine("Switch to FileStream for " + ForFile);
                m_FileStream = new FileStream(FileName, FileMode.Create);
                m_MemoryStream.WriteTo(m_FileStream);
                m_MemoryStream.Dispose();
                m_MemoryStream = null;
                m_StreamType = StreamType.File;
            }
            Stream.Write(buffer, offset, count);
        }

        public override void WriteByte(byte value)
        {
            Stream.WriteByte(value);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return Stream.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            Stream.SetLength(value);
        }

        public override long Length
        {
            get { return Stream.Length; }
        }

        bool m_Disposed = false;

        protected override void Dispose(bool disposing)
        {
            if (m_Disposed)
                return;

            if (disposing)
            {
                if (m_FileStream != null)
                {
                    Util.Loguj("Dispose FileStream " + FileName + " for " + ForFile);
                    //Debug.WriteLine("Dispose FileStream " + FileName);
                    m_FileStream.Dispose();
                    m_FileStream = null;
                }
                if (m_MemoryStream != null)
                {
                    //Debug.WriteLine("Dispose MemoryStream" + FileName);
                    m_MemoryStream.Dispose();
                    m_MemoryStream = null;
                }
                m_Disposed = true;
            }

            base.Dispose(disposing);

        }
    }
}
